/**
 * @file W25Q128_Demo.c
 * @brief
 * @details
 * @author Mars
 * @date 2023-10-22
 * @version V1.0.0
 * @copyright
 */

#include "f1c100s.h"
#include "w25q128.h"

//--------------------------------------------------------------------------------------------------------
//--------------------------------------W25Q128读写------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
#define W25Q256_Enter_4_Addres 0xB7
#define W24Q256_Exit_4_Addres 0xE9

// Ö¸Áî±í
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04

#define W25X_ReadStatusReg 0x05
#define W25X_ReadStatusReg1 0x05
#define W25X_ReadStatusReg2 0x35
#define W25X_ReadStatusReg3 0x15

#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_ReadData_4ADDR 0X13
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_PageProgram_4ADDR 0X12
// #define W25X_BlockErase     0xD8
#define W25X_SectorErase 0xd8
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F

#define W25X_FastRead_QUAD 0x6B
#define W25X_PageProgram_QUAD 0x32

#define FLASH_PAGE_SIZE 256
#define FLASH_BLOCK_SIZE 4096

int addr_mode = 0; // 地址模式-【默认0=3位地址】【1=4位地址】-大于16MB时使用4位地址

/***********************************************
Reg0
    //BIT7  6   5   4   3   2   1   0
    //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    //SPR:默认0,状态寄存器保护位,配合WP使用
    //TB,BP2,BP1,BP0:FLASH区域写保护设置
    //WEL:写使能锁定
    //BUSY:忙标记位(1,忙;0,空闲)
    //默认:0x00
***********************************************/
/***********************************************
Reg1
    //BIT7  6   5   4   3   2   1   0
    //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    //SPR:默认0,状态寄存器保护位,配合WP使用
    //TB,BP2,BP1,BP0:FLASH区域写保护设置
    //WEL:写使能锁定
    //BUSY:忙标记位(1,忙;0,空闲)
    //默认:0x00
***********************************************/
/***********************************************
Reg3
    //BIT7  6   5   4   3   2   1   0
    // (R) DRV1 DRV0(R)(R) WPS ADP ADS
  //  ADS=地址模式位 默认0   1=4位地址，否则3位地址 16MB以上型号会使用
***********************************************/

// 读取SPI_FLASH的状态寄存器
u8 SPI_Flash_ReadSR(int SPI, char Reg)
{
    u8_t addr_buff[4];
    u8 buf;
    volatile int res = 0;
    addr_buff[0] = Reg;
    sys_spi_select(SPI);
    if (sys_spi_write_then_read(SPI, addr_buff, 1, (void *)&buf, 1) < 0)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    return buf;
}

// 等待空闲
void SPI_Flash_Wait_Busy(int SPI)
{
    // 等待BUSY位清空，最低位为空闲位
    while ((SPI_Flash_ReadSR(SPI, W25X_ReadStatusReg1) & 0x01) == 0x01)
    {
    }
}
// SPI_FLASH写使能
// 将WEL置位
int SPI_FLASH_Write_Enable(int SPI)
{
    u8_t addr_buff[4];
    int res = 0;
    addr_buff[0] = W25X_WriteEnable;
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, 1) != 1)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    return res;
}
// SPI_FLASH写禁止
// 将WEL清零
int SPI_FLASH_Write_Disable(int SPI)
{
    u8_t addr_buff[4];
    int res = 0;
    addr_buff[0] = W25X_WriteDisable;
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, 1) != 1)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    return res;
}
/*擦除
 */
int SPI_Flash_Erase_Sector(int SPI, int addr)
{
    u8_t addr_buff[5];
    int buff_len = 0;
    int res = 0;
    if (addr_mode == 0) // 3地址
    {
        addr_buff[0] = 0x20;
        addr_buff[1] = (u8_t)(addr >> 16);
        addr_buff[2] = (u8_t)(addr >> 8);
        addr_buff[3] = (u8_t)(addr >> 0);
        buff_len = 4;
    }
    else // 4地址
    {
        addr_buff[0] = 0x21;
        addr_buff[1] = (u8_t)(addr >> 24);
        addr_buff[2] = (u8_t)(addr >> 16);
        addr_buff[3] = (u8_t)(addr >> 8);
        addr_buff[4] = (u8_t)(addr >> 0);
        buff_len = 5;
    }
    // 写使能
    if (SPI_FLASH_Write_Enable(SPI) < 0)
    {
        res = -1;
    }
    SPI_Flash_Wait_Busy(SPI);
    // 擦除
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, buff_len) != buff_len)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);

    SPI_Flash_Wait_Busy(SPI);

    return res;
}
/*擦除32K块-不支持4位地址
 */
int SPI_Flash_Erase_Block32K(int SPI, int addr)
{
    u8_t addr_buff[4];
    int res = 0;
    if (addr_mode == 1) // 4地址
    {
        sysprintf("ERR:Flash_Erase_Block32K 4 addresses are not supported!!!\r\n");
        return -1;
    }
    addr_buff[0] = 0x52;
    addr_buff[1] = (u8_t)(addr >> 16);
    addr_buff[2] = (u8_t)(addr >> 8);
    addr_buff[3] = (u8_t)(addr >> 0);

    // 写使能
    if (SPI_FLASH_Write_Enable(SPI) < 0)
    {
        res = -1;
    }
    SPI_Flash_Wait_Busy(SPI);
    // 擦除
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, 4) != 4)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);

    SPI_Flash_Wait_Busy(SPI);

    return res;
}
/*擦除64K块
 */
int SPI_Flash_Erase_Block64K(int SPI, int addr)
{
    u8_t addr_buff[5];
    int buff_len = 0;
    int res = 0;
    if (addr_mode == 0) // 3地址
    {
        addr_buff[0] = 0xD8;
        addr_buff[1] = (u8_t)(addr >> 16);
        addr_buff[2] = (u8_t)(addr >> 8);
        addr_buff[3] = (u8_t)(addr >> 0);
        buff_len = 4;
    }
    else // 4地址
    {
        addr_buff[0] = 0xDC;
        addr_buff[1] = (u8_t)(addr >> 24);
        addr_buff[2] = (u8_t)(addr >> 16);
        addr_buff[3] = (u8_t)(addr >> 8);
        addr_buff[4] = (u8_t)(addr >> 0);
        buff_len = 5;
    }
    // 写使能
    if (SPI_FLASH_Write_Enable(SPI) < 0)
    {
        res = -1;
    }
    SPI_Flash_Wait_Busy(SPI);
    // 擦除
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, buff_len) != buff_len)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);

    SPI_Flash_Wait_Busy(SPI);

    return res;
}
/*读UID 64Bit 8Byte
 */
int SPI_Flash_Read_UID(int SPI, unsigned char *rbuff)
{
    u8_t wbuff[5];
    int res = 0;
    wbuff[0] = 0x4B;
    wbuff[1] = 0x0;
    wbuff[2] = 0x0;
    wbuff[3] = 0x0;
    wbuff[4] = 0x0;
    // 命令
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, wbuff, NULL, 5) != 5)
    {
        {
            res = -1;
        }
    }
    if (sys_spi_transfer(SPI, NULL, rbuff, 8) != 8)
    {
        {
            res = -1;
        }
    }
    sys_spi_deselect(SPI);

    SPI_Flash_Wait_Busy(SPI);

    return res;
}
// 读设备ID
unsigned char SPI_Flash_Read_Device_ID(int SPI)
{
    u8_t addr_buff[4];
    u8_t buf[4];
    unsigned char device_id = 0;

    addr_buff[0] = 0xAB;
    sys_spi_select(SPI);
    if (sys_spi_write_then_read(SPI, addr_buff, 1, &buf, 4) < 0)
    {
        device_id = 0;
    }
    sys_spi_deselect(SPI);
    device_id = buf[3];
    return device_id;
}

/*
进入4字节地址模式-容量读写大于16MB需要改为4字节地址模式
*/
int spi_flash_enable_4_addres(int SPI)
{
    volatile int res = 0;
    u8_t addr_buff[4];
    addr_buff[0] = W25Q256_Enter_4_Addres;
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, 1) != 1)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    // 判断是不是进入了4位地址模式
    if ((SPI_Flash_ReadSR(SPI, W25X_ReadStatusReg3) & 0x01) == 0x01)
    {
        addr_mode = 1; // 4字节地址模式
        // sysprintf("enable 4 addr mode!!!\r\n");
        return 0;
    }
    else
    {
        sysprintf("ERR: enable 4 addr mode!!!\r\n");
        return -1;
    }
}
/*
退出4字节地址模式-容量读写大于16MB需要改为4字节地址模式
*/
int spi_flash_disable_4_addres(int SPI)
{
    volatile int res = 0;
    u8_t addr_buff[4];
    if (addr_mode == 0)
    {
        sysprintf("ERR:Current 3 addr mode!!!\r\n");
        return -1;
    }
    addr_buff[0] = W24Q256_Exit_4_Addres;
    sys_spi_select(SPI);
    if (sys_spi_transfer(SPI, addr_buff, NULL, 1) != 1)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);

    // 判断是不是进入了4位地址模式
    if ((SPI_Flash_ReadSR(SPI, W25X_ReadStatusReg3) & 0x01) == 0x00)
    {
        addr_mode = 0; // 3字节地址模式
        // sysprintf("disable 4 addr mode!!!\r\n");
        return 0;
    }
    else
    {
        sysprintf("ERR: disable 4 addr mode!!!\r\n");
        return -1;
    }
}
/*SPI读
 */
int SPI_Flash_Read(int SPI, int addr, void *buf, int count)
{
    u8_t addr_buff[5];
    int buff_len = 0;
    int res = 0;
    if (addr_mode == 0) // 3地址
    {
        addr_buff[0] = W25X_ReadData;
        addr_buff[1] = (u8_t)(addr >> 16);
        addr_buff[2] = (u8_t)(addr >> 8);
        addr_buff[3] = (u8_t)(addr >> 0);
        buff_len = 4;
    }
    else // 4地址
    {
        addr_buff[0] = W25X_ReadData_4ADDR; // 0x13
        addr_buff[1] = (u8_t)(addr >> 24);
        addr_buff[2] = (u8_t)(addr >> 16);
        addr_buff[3] = (u8_t)(addr >> 8);
        addr_buff[4] = (u8_t)(addr >> 0);
        buff_len = 5;
    }
    sys_spi_select(SPI);
    if (sys_spi_write_then_read(SPI, addr_buff, buff_len, buf, count) < 0)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    return res;
}

/*SPI写
 */
int sys_spi_flash_write(int SPI, int addr, void *buf, int count)
{
    u8_t addr_buff[5];
    int buff_len = 0;
    int res = 0;
    if (addr_mode == 0) // 3地址
    {
        addr_buff[0] = W25X_PageProgram;
        addr_buff[1] = (u8_t)(addr >> 16);
        addr_buff[2] = (u8_t)(addr >> 8);
        addr_buff[3] = (u8_t)(addr >> 0);
        buff_len = 4;
    }
    else // 4地址
    {
        addr_buff[0] = W25X_PageProgram_4ADDR; // 0x12
        addr_buff[1] = (u8_t)(addr >> 24);
        addr_buff[2] = (u8_t)(addr >> 16);
        addr_buff[3] = (u8_t)(addr >> 8);
        addr_buff[4] = (u8_t)(addr >> 0);
        buff_len = 5;
    }
    // 写使能
    if (SPI_FLASH_Write_Enable(SPI) < 0)
    {
        res = -1;
    }
    SPI_Flash_Wait_Busy(SPI);
    // 写
    sys_spi_select(SPI);
    if (sys_spi_write_then_write(SPI, addr_buff, buff_len, buf, count) < 0)
    {
        res = -1;
    }
    sys_spi_deselect(SPI);
    SPI_Flash_Wait_Busy(SPI);
    return res;
}

// 无检验写SPI FLASH
// 必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
// 具有自动换页功能
// 在指定地址开始写入指定长度的数据,但是要确保地址不越界!
// pBuffer:数据存储区
// WriteAddr:开始写入的地址(24bit)
// NumByteToWrite:要写入的字节数(最大65535)
// CHECK OK
void SPI_Flash_Write_NoCheck(int SPI, u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
    u16 pageremain;
    pageremain = FLASH_PAGE_SIZE - WriteAddr % FLASH_PAGE_SIZE; // 单页剩余的字节数
    if (NumByteToWrite <= pageremain)
    {
        pageremain = NumByteToWrite; // 不大于256个字节
    }
    while (1)
    {
        sys_spi_flash_write(SPI, WriteAddr, (void *)pBuffer, pageremain);
        if (NumByteToWrite == pageremain)
        {
            break; // 写入结束了
        }
        else // NumByteToWrite>pageremain
        {
            pBuffer += pageremain;   // 加上前一页的写和数量得到下页写数据的开始
            WriteAddr += pageremain; // 加上前一页的写的地此得到下页写地此的开始

            NumByteToWrite -= pageremain; // 减去已经写入了的字节数
            if (NumByteToWrite > FLASH_PAGE_SIZE)
            {
                pageremain = FLASH_PAGE_SIZE; // 一次可以写入256个字节
            }
            else
            {
                pageremain = NumByteToWrite; // 不够256个字节了，，，，，
            }
        }
    }
}

// 带擦除操作的写FLASH
// 在指定地址开始写入指定长度的数据
// pBuffer:数据存储区
// WriteAddr:开始写入的地址(24bit)
// NumByteToWrite:要写入的字节数(最大65535)
__align(4) u8 SPI_FLASH_BUF[FLASH_BLOCK_SIZE];
void SPI_Flash_Write(int SPI, unsigned char *pBuffer, unsigned int WriteAddr, unsigned int NumByteToWrite)
{
    u32 section_pos;
    u16 section_offset;
    u16 section_remain;
    u16 i;

    section_pos = WriteAddr / FLASH_BLOCK_SIZE;         // 扇区地址 0~511 for w25x16 ，4K一个扇区
    section_offset = WriteAddr % FLASH_BLOCK_SIZE;      // 在扇区内的偏移
    section_remain = FLASH_BLOCK_SIZE - section_offset; // 扇区空间大小
    if (NumByteToWrite <= section_remain)
    {
        section_remain = NumByteToWrite; // 不大于4096个字节，小于一个扇区时得到剩余空间
    }

    while (1)
    {
        // 读出整个扇区的内容
        SPI_Flash_Read(SPI, section_pos * FLASH_BLOCK_SIZE, (void *)SPI_FLASH_BUF, FLASH_BLOCK_SIZE);
        for (i = 0; i < section_remain; i++) // 校验数据
        {
            if (SPI_FLASH_BUF[section_offset + i] != 0XFF)
            {
                break; // 判断是否需要擦除
            }
        }

        if (i < section_remain) // 需要擦除
        {
            SPI_Flash_Erase_Sector(SPI, section_pos * FLASH_BLOCK_SIZE); // 擦除这个扇区 W25Q128要x4096
            for (i = 0; i < section_remain; i++)                         // 复制
            {
                SPI_FLASH_BUF[i + section_offset] = pBuffer[i]; // 把数据复制到一个扇区的剩余空间，
            }
            // 写入整个扇区 ，以便前面擦除的的数据能还原
            SPI_Flash_Write_NoCheck(SPI, SPI_FLASH_BUF, section_pos * FLASH_BLOCK_SIZE, FLASH_BLOCK_SIZE);
        }
        else
        {
            // 写已经擦除了的,直接写入扇区剩余区间.
            SPI_Flash_Write_NoCheck(SPI, pBuffer, WriteAddr, section_remain);
        }
        if (NumByteToWrite == section_remain)
        {
            // 写入结束了
            break;
        }
        else // 写入未结束
        {
            section_pos++;      // 扇区地址增1
            section_offset = 0; // 第二次写入时，偏移位置为0

            pBuffer += section_remain;        // 指针偏移
            WriteAddr += section_remain;      // 写地址偏移
            NumByteToWrite -= section_remain; // 字节数递减
            if (NumByteToWrite > FLASH_BLOCK_SIZE)
            {
                section_remain = FLASH_BLOCK_SIZE; // 下一个扇区还是写不完
            }
            else
            {
                section_remain = NumByteToWrite; // 下一个扇区可以写完了
            }
        }
    }
}

/****************************************************
W25Q128_spi测试
SPI 数据小于等于64Byte时使用CPU模式，SPI 数据大于64Byte时使用DMA模式;
sys_spi_transfer里面也可改成单CPU模式或单DMA模式;
测试SPI-FLASH使用板上存储程序的SPI-FLASH，测试起始地址不能为程序区;
****************************************************/
#define TESTSIZE (1 * 256 * 1024)

void W25Q128_Demo(void)
{
    unsigned char id_buff[8];
    int SPI = SPI0, i, s = 0, addr = (1024 * 1024 * 4);
    u8 buff_in[TESTSIZE];
    u8 buff_out[TESTSIZE];

    /*------数据初始化------*/
    for (i = 0; i < TESTSIZE; i++)
    {
        buff_in[i] = i & 0xff;
        buff_out[i] = 0;
    }

    /*------SPI初始化------*/
    sys_spi_init(SPI);
    sysprintf("SPI%d_Init...\r\n", SPI);

    sysprintf("SPI_Demo W25Q128...\r\n");
    char did = SPI_Flash_Read_Device_ID(SPI);

    /*
        0XEF13,表示芯片型号为W25Q80
        0XEF14,表示芯片型号为W25Q16
        0XEF15,表示芯片型号为W25Q32
        0XEF16,表示芯片型号为W25Q64
        0XEF17,表示芯片型号为W25Q128
    */

    switch (did)
    {
    case 0x13:
        sysprintf("W25Q80\r\n");
        break;
    case 0x14:
        sysprintf("W25Q16 2M\r\n");
        break;
    case 0x15:
        sysprintf("W25Q32 4M\r\n");
        break;
    case 0x16:
        sysprintf("W25Q64 8M\r\n");
        break;
    case 0x17:
        sysprintf("W25Q128 16M\r\n");
        break;
    default:
        sysprintf("NULL\r\n");
        return;
        break;
    }

    if (did > 0x17) // 容量大于16MB，设置为4字节地址模式，设备ID=0x17为W25Q128(16MB)
    {
        spi_flash_enable_4_addres(SPI);
        sysprintf("addr=4byte\r\n");
    }
    SPI_Flash_Read_UID(SPI0, id_buff);
    sysprintf("DID=%02X,UID:%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n", did,
              id_buff[0], id_buff[1], id_buff[2], id_buff[3], id_buff[4], id_buff[5], id_buff[6], id_buff[7]);

    /*------写入------*/
    sysprintf("SPI 写入...\r\n");
    SPI_Flash_Write(SPI, buff_in, addr, TESTSIZE);
    /*------读出------*/
    sysprintf("SPI_读出...\r\n");
    SPI_Flash_Read(SPI, addr, buff_out, TESTSIZE);
    /*------校验------*/
    for (i = 0; i < TESTSIZE; i++)
    {
        // sysprintf("read[%d]:[%d]-[%d]\r\n", i, buff_in[i], buff_out[i]);
        if (buff_in[i] == buff_out[i])
        {
            s++;
        }
    }
    if (s == TESTSIZE)
    {
        sysprintf("读写正确\r\n");
    }
    else
    {
        sysprintf("读写错误\r\n");
    }
    // 退出4字节地址模式
    if (addr_mode == 1)
    {
        spi_flash_disable_4_addres(SPI);
    }
}

// End W25Q128_Demo.c
